﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace FormConvertor
{
    public class FormConverter : IDisposable
    {
        Regex _rComment = new Regex("(?<=^\\s+//\\s+)(?<varname>\\w+)\\s*$");
        Regex _rFormName = new Regex("(?<=(partial\\s+class\\s+))\\w+");
        Regex _rDeclare = new Regex("this\\.\\w+\\s+=\\s+new\\s+System.Windows.Forms");
       
        Regex _rDeclareRemplace = new Regex("(?<declarePart>(?<=new\\s+).+(?=\\())");
        Regex _rDeclareVarName = new Regex("(?<declareVar>(?<=this\\.)\\w+(?=\\s*))");

        Regex _rInitRemplace = new Regex("this(\\.\\w+){2,}\\s+(=)");
        Regex _rInitVarname = new Regex("(?<varname>(?<=this\\.)\\w+?(?=\\.))");
        Regex _rInitFieldname = new Regex("(?<fieldname>(?<=\\.)\\w+?(?=\\s*(=)))");
        Regex _rVariableName = new Regex("(?<=\\.)\\w+?(?=\\s*=)");

        Regex _rFormInitVarname = new Regex("^\\s+this\\.(?<fieldname>\\w+?(?=\\s*(=)))");
        Regex _rFormAddCtrl = new Regex("(?<=(^\\s*this\\.Controls\\.Add\\(this\\.))(?<varname>\\w+)?(?=\\);\\s*$)");

        Regex _rDefinition = new Regex("^\\s*(?<range>(private|public|protected))\\s*(?<ctrlType>System\\.Windows\\.Forms\\.\\w+)\\s+(?<varname>\\w+);\\s*$");

        String _formSourceName;
        String Formouput;
        String DeclaringPart = String.Empty;
        String InitCtrlsPart = String.Empty;
        String InitFormPart = String.Empty;
        String DefiningPart = String.Empty;
        public String FinalCode = null;
        public Dictionary<int, String> warnings = new Dictionary<int, string>();
        String inputFilePath;
        Dictionary<String, object> _checkboxes = new Dictionary<string, object>();
        StreamReader reader;
        int lineIndex = 0;

        static Dictionary<String, Dictionary<String,String>> _ConversionFields = InitDictionnaryType();

        private static Dictionary<string, Dictionary<String, String>> InitDictionnaryType()
        {
            Dictionary<String, Dictionary<String, String>> result = new Dictionary<String, Dictionary<String, String>>();

            //CheckBox
            String curtype = "System.Windows.Forms.CheckBox";
            Dictionary<String, String> conversiondico = new Dictionary<String, String>();
            conversiondico.Add("ForeColor", "CheckColor");

            result.Add(curtype, conversiondico);


            return result;
        }

        static List<String> _NonSupportedCtrlFields = InitNonSupportedFields();

        private static List<string> InitNonSupportedFields()
        {
            List<String> result = new List<string>();
            result.Add("AutoSize");
            result.Add("Font");
            result.Add("TabIndex");
            result.Add("UseVisualStyleBackColor");
            result.Add("AutoScaleDimensions");
            result.Add("Margin");
            result.Add("AutoScaleMode");
            result.Add("FormattingEnabled");
            result.Add("ItemHeight");
            result.Add("FormattingEnabled");
            
            return result;
        }

   

        public bool ReadDesigner(String filePath)
        {
            bool result = true;
            if (String.IsNullOrEmpty(filePath))
                throw new ArgumentNullException("filePath");
            if (!File.Exists(filePath))
                throw new FileNotFoundException("filePath");
            if(!filePath.EndsWith("Designer.cs"))
                throw new ArgumentException("you must select the *.Designer.cs file");
            inputFilePath = filePath;
            using (reader = new StreamReader(inputFilePath))
            {
                lineIndex = 0;
                PrepareFormDeclaration();
                while (!reader.EndOfStream)
                {
                    result = ExtractLine();
                    if (!result)
                        break;
                }
            }
            FinalCode = Formouput + DeclaringPart + InitCtrlsPart + InitFormPart;
            FinalCode += "\r\n        }\r\n\r\n";
            FinalCode += DefiningPart;
            FinalCode += "\r\n    }\r\n";
            
            return result;

        }

        private bool ExtractLine()
        {
            bool result = true;
            String line = reader.ReadLine();
            lineIndex++;
            if (_rDeclare.IsMatch(line))
            {
                ConvertDeclaringLine(line);
            }
            else if (_rComment.IsMatch(line))
            {
                String varname = _rComment.Match(line).Groups["varname"].Value;
                if (varname == _formSourceName || variables.ContainsKey(varname))
                {
                    InitCtrlsPart += _rComment.Replace(line, "\r\n");
                    InitCtrlsPart += line + "\r\n";
                    InitCtrlsPart += _rComment.Replace(line, "\r\n");
                }

            }
            else if (_rInitRemplace.IsMatch(line))
                ConvertInitLine(line);
            else if (_rFormInitVarname.IsMatch(line))
                ConvertFormInitLine(line);
            else if (_rFormAddCtrl.IsMatch(line))
                ConvertFormAddCrl(line);
            else if (_rDefinition.IsMatch(line))
                ConvertCtrlDefinitions(line);
            
            return result;
        }

        

        private void ConvertFormAddCrl(string line)
        {
            String varname = _rFormAddCtrl.Match(line).Groups["varname"].Value;
            if (variables.Keys.Contains(varname))
                InitFormPart += line + "\r\n";
        }

        

        private void ConvertInitLine(String line)
        {
            String variable = _rInitVarname.Match(line).Value;
            String field = _rInitFieldname.Match(line).Value;


            if (!_NonSupportedCtrlFields.Contains(field))
            {
                InitCtrlsPart += line+"\r\n";
            }
            else
                warnings.Add(lineIndex, String.Format("Field '{0}' ignored : it is not supported by GTA Controls", field));
        }

       

        List<String> ignoredvars = new List<string>();
        Dictionary<String, String> variables = new Dictionary<String, String>();

        private void ConvertFormInitLine(string line)
        {
            String field = _rFormInitVarname.Match(line).Groups["fieldname"].Value;
           
            if (!_NonSupportedCtrlFields.Contains(field))
            {
                String foundType = _rDeclareRemplace.Match(line).Value;
                //TODO : à revoir et finir je suis claqué
                //TODO : la ca va pas, faut d'abord voir si la partie de droite contient un new blabla avant d'utiliser CastType, si pas de new, pas besoin de casttype
                String convertedType = CastType(foundType);
                if (convertedType == null) //No cast found
                {
                    if (field == "ClientSize")
                    {
                        String newline = line.Replace(field, "Size");
                        Regex reg = new Regex("(?<=,\\s*)\\d+?(?=\\s*\\);\\s*$)");
                        int newH = Int32.Parse(reg.Match(newline).Value) + 24;
                        InitFormPart += reg.Replace(newline, newH.ToString()) + "\r\n";
                    }
                    else
                        InitFormPart += line + "\r\n";
                }
                else
                    InitFormPart += _rDeclareRemplace.Replace(line, convertedType) + "\r\n";

                //TODO ajouter aussi, si c'est this.Text le dupliquer en this.Name

            }
            else
                warnings.Add(lineIndex, String.Format("Field '{0}' ignored : it is not supported by GTA Controls", field));

        }

        private void ConvertCtrlDefinitions(string line)
        {
            String result;
            String range = _rDefinition.Match(line).Groups["range"].Value;

            if (range == "private")
                result = line.Replace(range, "protected");
            else
                result = line;

            String inputType = _rDefinition.Match(result).Groups["ctrlType"].Value;
            
            String convertedType = CastType(inputType);
            if (convertedType == null)
                DefiningPart += result + "\r\n";
            else
                DefiningPart += result.Replace(inputType, convertedType)+"\r\n";
        }

        private void ConvertDeclaringLine(String line)
        {
            String foundType = _rDeclareRemplace.Match(line).Value;
            String varname = _rDeclareVarName.Match(line).Value;

            String convertedType = CastType(foundType);
            if (convertedType == null) //Cast not found
            {
                warnings.Add(lineIndex, String.Format("variable {0} will be excluded because the type '{1}' cannot be converted to a GTA.Forms control.", varname, foundType));
                ignoredvars.Add(varname);
                return;
            }
            else
            {
                DeclaringPart += _rDeclareRemplace.Replace(line, convertedType) + "\r\n"; ;
                try
                {
                    variables.Add(varname, foundType);
                }
                catch (ArgumentException) //we don't care if allready filled
                {
                }
            }
        }

        private string CastType(String inputType)
        {
            
            
            switch (inputType)
            {
                case "System.Windows.Forms.CheckBox":
                    return "GTA.Forms.Checkbox";
                case "System.Windows.Forms.Button":
                    return "GTA.Forms.Button";
                case "System.Windows.Forms.ListBox":
                   return  "GTA.Forms.Listbox";
                case "System.Windows.Forms.Label":
                   return "GTA.Forms.Label";
                case "System.Windows.Forms.TextBox":
                   return "GTA.Forms.Textbox";
            }
            return null;
        }

        public  string ReplaceNew(Match match)
        {
            return null;
        }


        

        private void PrepareFormDeclaration()
        {
            if (reader.EndOfStream)
                throw new InvalidDataException("the file seems empty");
            String line = reader.ReadLine();
            lineIndex++;
            while (!_rFormName.IsMatch(line))
            {
                line = reader.ReadLine();
                lineIndex++;
            }
            _formSourceName = _rFormName.Match(line).Value;
            Formouput = String.Format("    public abstract class {0}_Designer : GTA.Forms.Form\r\n    {1}\r\n\r\n        public {0}_Designer()\r\n        {1}\r\n", _formSourceName, "{");

        }

        #region IDisposable Members

        //public int FindDeclarations(String filePath)
        //{
        //    using (StreamReader reader = new StreamReader(filePath))
        //    {
        //        String line = reader.ReadLine();
        //        while (!_rDeclare.IsMatch(line))
        //        {
        //            line = reader.ReadLine();
        //        }

        //        while (_rDeclare.IsMatch(line))
        //        {
        //            if (_rNewCheckbox.IsMatch(line))
        //                _checkboxes.Add(_rVariableName.Match(line).Value, null);
        //            line = reader.ReadLine();
        //        }
        //        return _checkboxes.Count;
        //    }
        //}

        protected virtual void Dispose(bool nativeOnly)
        {
            Dispose();
        }

        public void Dispose()
        {
            if (reader != null)
                reader.Dispose();
            reader = null;
        }

        #endregion
    }
}
